﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace Charting
{
    /// <summary>
    /// 方向图图表
    /// </summary>
    public partial class Plot : UserControl
    {
        // 坐标类型
        private EPlotType mCoordType;
        // X轴
        private Axis mAxisX;
        // Y轴
        private Axis mAxisY;
        // 系列列表
        private List<Series> mSeries;
        // 标记线值
        private float mReferLine;
        // 图表与控件的边距
        private int mCoordPadding;
        // 直角坐标的图边界
        private RectangleF mLineCoord;
        // 极坐标的图边界
        private RectangleF mPolarCoord;
        // 图的背景刷
        private SolidBrush mBackBrush;
        // 图的背景刷
        private SolidBrush mCoordBackBrush;
        // 标记线画笔
        private Pen mReferLinePen;
        // 拾取线画笔
        private Pen mPickLinePen;
        // 启用拾取功能
        private bool mPickEnabled;
        // 图区显示的内容：坐标图 + 曲线图
        private Bitmap mContent; // 由 mCoord + n * Series.Bitmap
        // 坐标图
        private Bitmap mCoord;
        // 拾取游标
        private DataPoint[] mCursorPoint;
        // 批量更新标记
        private bool mBatchUpdate;
        // 批量更新计数
        private int mBatchUpdateCount;
        // 坐标更新标记
        private bool mUpdateCoord;
        // 系列点更新标记
        private bool mUpdateSeriesPoints;
        // 图区内容更新标记
        private bool mUpdateContent;
        //缺省的颜色
        private Color[] mForeColors = { Color.Red, Color.Blue, Color.Green, Color.Magenta, Color.Yellow, Color.Purple, Color.YellowGreen, Color.Brown, Color.Gold };
        public Plot()
        {
            InitializeComponent();

            
            mCoordType = EPlotType.Line;

            mCoordPadding = 30;

            int lineLeft = mCoordPadding;
            int lineTop = mCoordPadding;
            int lineWidth = this.Width - 2 * mCoordPadding - 1;
            int lineHeight = this.Height - 2 * mCoordPadding - 1;
            mLineCoord = new RectangleF(lineLeft, lineTop, lineWidth, lineHeight);

            int polarWidth = Math.Min(this.Width - 2 * mCoordPadding - 1, this.Height - 2 * mCoordPadding - 1);
            int polarHeight = polarWidth;
            int polarLeft = (this.Width - polarWidth)/2;
            int polarTop = mCoordPadding;
            mPolarCoord = new RectangleF(polarLeft, polarTop, polarWidth, polarHeight);

            mBackBrush = new SolidBrush(this.BackColor);
            mCoordBackBrush = new SolidBrush(Color.White);

            mReferLinePen = new Pen(Color.Black);
            mReferLinePen.DashStyle = DashStyle.Custom;
            mReferLinePen.DashPattern = new float[] { 6, 6 };

            mPickLinePen = new Pen(Color.Black);

            mAxisX = new Axis(this);
            mAxisY = new Axis(this);

            mReferLine = 5;

            mSeries = new List<Series>();
            mCoord = null;
            mPickEnabled = false;
            mContent = null;

            mCursorPoint = new DataPoint[2] { DataPoint.Empty, DataPoint.Empty };

            mBatchUpdate = false;
            mBatchUpdateCount = 0;
            mUpdateCoord = false;
            mUpdateSeriesPoints = false;
            mUpdateContent = false;
        }

        /// <summary>
        /// 设置和获取坐标类型
        /// </summary>
        public EPlotType PlotType
        {
            set
            {
                if (mCoordType == value)
                    return;
                mCoordType = value;
                UpdateCoord();
                UpdateSeriesPoints();
                UpdateContent();
            }

            get
            {
                return mCoordType;
            }
        }
        /// <summary>
        /// 是否显示图例
        /// </summary>
        public bool ShowLegend { get; set; }
        /// <summary>
        /// 获取X轴
        /// </summary>
        public Axis AxisX
        {
            get
            {
                return mAxisX;
            }
        }

        /// <summary>
        /// 获取Y轴
        /// </summary>
        public Axis AxisY
        {
            get
            {
                return mAxisY;
            }
        }

        /// <summary>
        /// 设置和获取标记线的大小
        /// </summary>
        public float ReferLineValue
        {
            set
            {
                if (mReferLine == value)
                    return;
                mReferLine = value;
                UpdateCoord();
                UpdateContent();
            }

            get
            {
                return mReferLine;
            }
        }

        /// <summary>
        /// 设置和获取图的背景色
        /// </summary>
        public Color CoordBackColor
        {
            set
            {
                if (mCoordBackBrush.Color == value)
                    return;
                mCoordBackBrush.Color = value;
                UpdateCoord();
                UpdateContent();
            }

            get
            {
                return mCoordBackBrush.Color;
            }
        }

        /// <summary>
        /// 设置和获取标记线的颜色
        /// </summary>
        public Color ReferLineColor
        {
            set
            {
                if (mReferLinePen.Color == value)
                    return;
                mReferLinePen.Color = value;
                UpdateCoord();
                UpdateContent();
            }

            get
            {
                return mReferLinePen.Color;
            }
        }

        /// <summary>
        /// 设置和获取标记线的大小
        /// </summary>
        public float ReferLineWidth
        {
            set
            {
                if (mReferLinePen.Width == value)
                    return;
                mReferLinePen.Width = value;
                UpdateCoord();
                UpdateContent();
            }

            get
            {
                return mReferLinePen.Width;
            }
        }

        public bool PickEnabled
        {
            set
            {
                if (mPickEnabled == value)
                    return;
                mPickEnabled = value;
                UpdateContent();
            }

            get
            {
                return mPickEnabled;
            }
        }

        /// <summary>
        /// 设置和获取标记线的颜色
        /// </summary>
        public Color PickLineColor
        {
            set
            {
                mPickLinePen.Color = value;
            }

            get
            {
                return mPickLinePen.Color;
            }
        }

        /// <summary>
        /// 设置和获取标记线的大小
        /// </summary>
        public float PickLineWidth
        {
            set
            {
                mPickLinePen.Width = value;
            }

            get
            {
                return mPickLinePen.Width;
            }
        }

        /// <summary>
        /// 设置和获取图与控件的边距
        /// </summary>
        public int CoordPadding
        {
            set
            {
                if (mCoordPadding == value)
                    return;
                mCoordPadding = value;
                UpdateCoordBounds();
            }

            get
            {
                return mCoordPadding;
            }
        }

        public RectangleF CoordBounds
        {
            get
            {
                if (mCoordType == EPlotType.Line)
                    return mLineCoord;
                return mPolarCoord;
            }
        }

        public Series NewSeries()
        {
            Series series = new Series(this);
            if(mSeries.Count<mForeColors.Length-1)
            {
                series.LineColor = mForeColors[mSeries.Count];
            }
            else
            {
                Random random = new Random();
                series.LineColor = Color.FromArgb(random.Next(0, Int32.MaxValue));               
            }
            series.LabelBackColor = BackColor;
            series.LabelForeColor = series.LineColor;
            series.PointColor = series.LineColor;
            mSeries.Add(series);
            return series;
        }

        public void RemoveSeries(Series series)
        {
            mSeries.Remove(series);
        }

        public int SeriesCount
        {
            get
            {
                return mSeries.Count;
            }
        }

        public Series SeriesAt(int index)
        {
            return mSeries[index];
        }

        public void ClearSeries()
        {
            if (mSeries.Count == 0)
                return;
            foreach (Series se in mSeries)
                se.Dispose();
            mSeries.Clear();
            UpdateContent();
        }

        public void BeginUpdate()
        {
            mBatchUpdate = true;
            ++mBatchUpdateCount;
        }

        public void EndUpdate()
        {
            --mBatchUpdateCount;
            if (mBatchUpdateCount > 0)
                return;
            mBatchUpdate = false;
            if (mUpdateCoord)
                UpdateCoord();
            if (mUpdateSeriesPoints)
                UpdateSeriesPoints();
            if (mUpdateContent)
                UpdateContent();
            mUpdateCoord = false;
            mUpdateSeriesPoints = false;
            mUpdateContent = false;
        }

        /// <summary>
        /// 坐标转换为图中的位置
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public PointF CoordToPosition(float x, float y)
        {
            PointF result = new PointF(x, y);
            if (mCoordType == EPlotType.Line)
            {
                result.X = mLineCoord.X
                    + (x - mAxisX.Minimum) * mLineCoord.Width / (mAxisX.Maximum - mAxisX.Minimum);
                result.Y = mLineCoord.Y + mLineCoord.Height
                    - (y - (float)mAxisY.Minimum) * mLineCoord.Height / (mAxisY.Maximum - mAxisY.Minimum);
            }
            else
            {
                float centerX = mPolarCoord.X + mPolarCoord.Width / 2;
                float centerY = mPolarCoord.Y + mPolarCoord.Height / 2;
                float angle = (x - mAxisX.Minimum) * 360.0F / (mAxisX.Maximum - mAxisX.Minimum);
                float radius = (y - mAxisY.Minimum) * mPolarCoord.Width / (2 * (mAxisY.Maximum - mAxisY.Minimum));

                result.X = centerX - (float)Math.Sin(angle * Math.PI / 180) * radius;
                result.Y = centerY + (float)Math.Cos(angle * Math.PI / 180) * radius;
            }
            return result;
        }

        /// <summary>
        /// 图中的位置转换为坐标
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public PointF PositionToCoord(float x, float y)
        {
            PointF result = new PointF(x, y);
            if (mCoordType == EPlotType.Line)
            {
                result.X = (x - mLineCoord.X) * (mAxisX.Maximum - mAxisX.Minimum) / mLineCoord.Width
                    + (float)mAxisX.Minimum;
                result.Y = (mLineCoord.Y + mLineCoord.Height - y) * (mAxisY.Maximum - mAxisY.Minimum) / mLineCoord.Height
                    + mAxisY.Minimum;
            }
            else
            {
                float centerX = mPolarCoord.X + mPolarCoord.Width / 2;
                float centerY = mPolarCoord.Y + mPolarCoord.Height / 2;
                float radian = (float)(Math.Atan2(x - centerX, centerY - y) + Math.PI);

                result.X = (radian * 180.0F / (float)Math.PI) * (mAxisX.Maximum - mAxisX.Minimum) / 360 + mAxisX.Minimum;
                result.Y = 2 * (float)Math.Abs((x - centerX) / Math.Sin(radian)) * (mAxisY.Maximum - mAxisY.Minimum) / mPolarCoord.Width + mAxisY.Minimum;
            }
            return result;
        }
        /// <summary>
        /// 获取指定坐标点的数据值。
        /// </summary>
        /// <param name="x">坐标点x</param>
        /// <param name="y">坐标点y</param>
        /// <param name="drawPoint">是否绘制坐标线</param>
        /// <returns></returns>
        public DataPoint[] GetCursorPoint(int x, int y, bool drawPoint)
        {
            mCursorPoint[0] = DataPoint.Empty;
            mCursorPoint[1] = DataPoint.Empty;

            if (!mPickEnabled)
                return mCursorPoint;

            int index1 = -1;
            for (int i = 0; i < mSeries.Count; ++i)
            {
                if (mSeries[i].Visible && mSeries[i].Points.Count > 0)
                {
                    index1 = i;
                    break;
                }
            }
            if (index1 == -1)
                return mCursorPoint;

            int index2 = -1;
            for (int i = 0; i < mSeries.Count; ++i)
            {
                if (i != index1 && mSeries[i].Visible && mSeries[i].Points.Count > 0)
                {
                    index2 = i;
                    break;
                }
            }

            // 计算拾取点在坐标系中的坐标
            PointF pickPt = PositionToCoord(x, y);
            // 计算与拾取点的X坐标相同的游标坐标
            mCursorPoint[0] = mSeries[index1].Points.PointAt(pickPt.X);
            if (index2 != -1)
                mCursorPoint[1] = mSeries[index2].Points.PointAt(pickPt.X);
            if (mCursorPoint[0] == DataPoint.Empty)
                return mCursorPoint;

            if (!drawPoint)
                return mCursorPoint;

            if (mContent == null)
                return mCursorPoint;

            Bitmap destBmp = new Bitmap(this.Width, this.Height);
            Graphics g = Graphics.FromImage(destBmp);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.DrawImage(mContent, 0, 0);
            float crossPtSize = 4;
            if (mCoordType == EPlotType.Line)
            {
                // X轴游标线
                float lineX = x;
                if (lineX < mLineCoord.X)
                    lineX = mLineCoord.X;
                else if (lineX > mLineCoord.X + mLineCoord.Width)
                    lineX = mLineCoord.X + mLineCoord.Width;
                g.DrawLine(mPickLinePen, lineX, mLineCoord.Y, lineX, mLineCoord.Y + mLineCoord.Height);
                g.DrawLine(mPickLinePen, lineX, mLineCoord.Y, lineX, mLineCoord.Y + mLineCoord.Height);

                PointF linePt = CoordToPosition(pickPt.X, mCursorPoint[0].Y);
                if (linePt.Y < mLineCoord.Y)
                    linePt.Y = mLineCoord.Y;
                else if (linePt.Y > mLineCoord.Y + mLineCoord.Height)
                    linePt.Y = mLineCoord.Y + mLineCoord.Height;
                g.SetClip(mLineCoord);
                g.FillEllipse(mPickLinePen.Brush,
                    linePt.X - crossPtSize / 2, linePt.Y - crossPtSize / 2,
                    crossPtSize, crossPtSize);
                g.ResetClip();
                g.DrawLine(mPickLinePen, mLineCoord.X, linePt.Y, mLineCoord.X + mLineCoord.Width, linePt.Y);
                g.DrawLine(mPickLinePen, mLineCoord.X, linePt.Y, mLineCoord.X + mLineCoord.Width, linePt.Y);
            }
            else
            {
                float centerX = mPolarCoord.X + mPolarCoord.Width / 2;
                float centerY = mPolarCoord.Y + mPolarCoord.Height / 2;

                PointF lineEnd = CoordToPosition(pickPt.X, mAxisY.Maximum);
                g.DrawLine(mPickLinePen, centerX, centerY, lineEnd.X, lineEnd.Y);
                g.DrawLine(mPickLinePen, centerX, centerY, lineEnd.X, lineEnd.Y);

                float cursorY = mCursorPoint[0].Y;
                if (cursorY < mAxisY.Minimum)
                    cursorY = mAxisY.Minimum;
                PointF dataPos = CoordToPosition(pickPt.X, cursorY);
                if (dataPos.Y < mPolarCoord.Y)
                    dataPos.Y = mPolarCoord.Y;
                else if (dataPos.Y > mPolarCoord.Y + mPolarCoord.Height)
                    dataPos.Y = mPolarCoord.Y + mPolarCoord.Height;
                g.SetClip(mPolarCoord);
                g.FillEllipse(mPickLinePen.Brush,
                    dataPos.X - crossPtSize / 2, dataPos.Y - crossPtSize / 2,
                    crossPtSize, crossPtSize);
                g.ResetClip();

                float radius = (float)Math.Sqrt((dataPos.X - centerX) * (dataPos.X - centerX) + (dataPos.Y - centerY) * (dataPos.Y - centerY));
                g.DrawEllipse(mPickLinePen, centerX - radius, centerY - radius, 2 * radius, 2 * radius);
                g.DrawEllipse(mPickLinePen, centerX - radius, centerY - radius, 2 * radius, 2 * radius);
            }
            g.Dispose();
            // 双缓冲绘图
            Graphics draw = this.CreateGraphics();
            draw.DrawImage(destBmp, 0, 0);
            draw.Dispose();
            destBmp.Dispose();

            return mCursorPoint;
        }

        public List< DataPoint> GetCursorData(float x, float y, bool drawPoint)
        {
            List<DataPoint> dataPoints = new List<DataPoint>();

            if (!mPickEnabled)
                return dataPoints;

            // 计算拾取点在坐标系中的坐标
            PointF pickPt = PositionToCoord(x, y);
            // 计算与拾取点的X坐标相同的游标坐标
            foreach (var s in mSeries)
            {
                if (!s.Visible) continue;
                if (s.Points.Count <= 0) continue;
                DataPoint point = s.Points.PointAt(pickPt.X);
                dataPoints.Add(point);
            }

            if (drawPoint && mContent!=null)
            {
                Bitmap destBmp = new Bitmap(this.Width, this.Height);
                Graphics g = Graphics.FromImage(destBmp);
                g.SmoothingMode = SmoothingMode.AntiAlias;
                g.CompositingQuality = CompositingQuality.HighQuality;
                g.DrawImage(mContent, 0, 0);
                float crossPtSize = 4;
                if (mCoordType == EPlotType.Line)
                {
                    // X轴游标线
                    float lineX = x;
                    if (lineX < mLineCoord.X)
                        lineX = mLineCoord.X;
                    else if (lineX > mLineCoord.X + mLineCoord.Width)
                        lineX = mLineCoord.X + mLineCoord.Width;
                    g.DrawLine(mPickLinePen, lineX, mLineCoord.Y, lineX, mLineCoord.Y + mLineCoord.Height);
                    g.DrawLine(mPickLinePen, lineX, mLineCoord.Y, lineX, mLineCoord.Y + mLineCoord.Height);

                    PointF linePt = CoordToPosition(pickPt.X, dataPoints[0].Y);
                    if (linePt.Y < mLineCoord.Y)
                        linePt.Y = mLineCoord.Y;
                    else if (linePt.Y > mLineCoord.Y + mLineCoord.Height)
                        linePt.Y = mLineCoord.Y + mLineCoord.Height;
                    g.SetClip(mLineCoord);
                    g.FillEllipse(mPickLinePen.Brush,
                        linePt.X - crossPtSize / 2, linePt.Y - crossPtSize / 2,
                        crossPtSize, crossPtSize);
                    g.ResetClip();
                    g.DrawLine(mPickLinePen, mLineCoord.X, linePt.Y, mLineCoord.X + mLineCoord.Width, linePt.Y);
                    g.DrawLine(mPickLinePen, mLineCoord.X, linePt.Y, mLineCoord.X + mLineCoord.Width, linePt.Y);
                }
                else
                {
                    float centerX = mPolarCoord.X + mPolarCoord.Width / 2;
                    float centerY = mPolarCoord.Y + mPolarCoord.Height / 2;

                    PointF lineEnd = CoordToPosition(pickPt.X, mAxisY.Maximum);
                    g.DrawLine(mPickLinePen, centerX, centerY, lineEnd.X, lineEnd.Y);
                    g.DrawLine(mPickLinePen, centerX, centerY, lineEnd.X, lineEnd.Y);

                    float cursorY = dataPoints[0].Y;
                    if (cursorY < mAxisY.Minimum)
                        cursorY = mAxisY.Minimum;
                    PointF dataPos = CoordToPosition(pickPt.X, cursorY);
                    if (dataPos.Y < mPolarCoord.Y)
                        dataPos.Y = mPolarCoord.Y;
                    else if (dataPos.Y > mPolarCoord.Y + mPolarCoord.Height)
                        dataPos.Y = mPolarCoord.Y + mPolarCoord.Height;
                    g.SetClip(mPolarCoord);
                    g.FillEllipse(mPickLinePen.Brush,
                        dataPos.X - crossPtSize / 2, dataPos.Y - crossPtSize / 2,
                        crossPtSize, crossPtSize);
                    g.ResetClip();

                    float radius = (float)Math.Sqrt((dataPos.X - centerX) * (dataPos.X - centerX) + (dataPos.Y - centerY) * (dataPos.Y - centerY));
                    g.DrawEllipse(mPickLinePen, centerX - radius, centerY - radius, 2 * radius, 2 * radius);
                    g.DrawEllipse(mPickLinePen, centerX - radius, centerY - radius, 2 * radius, 2 * radius);
                }
                g.Dispose();
                // 双缓冲绘图
                Graphics draw = this.CreateGraphics();
                draw.DrawImage(destBmp, 0, 0);
                draw.Dispose();
                destBmp.Dispose();
            }
               
            return dataPoints;
        }
        /// <summary>
        /// 保存为图片
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="format"></param>
        public void SaveImage(string filename, ImageFormat format)
        {
            float x = -CoordBounds.X + mCoordPadding;
            int width = (int)CoordBounds.Width + 2 * mCoordPadding;
            int height = (int)CoordBounds.Height + 2 * mCoordPadding;

            Bitmap bitMap = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(bitMap);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.FillRectangle(Brushes.White, 0, 0, width, height);
            if (mCoord != null)
                g.DrawImage(mCoord, x, 0);
            foreach (Series se in mSeries)
                se.Draw(g, x, 0);
            bitMap.Save(filename, format);
            g.Dispose();
            bitMap.Dispose();
        }

        internal void UpdateCoord()
        {
            if (mBatchUpdate)
            {
                mUpdateCoord = true;
                mUpdateContent = true;
                return;
            }

            if (mCoord != null)
                mCoord.Dispose();
            mCoord = null;

            if (this.Width < 1 || this.Height < 1)
                return;

            mCoord = new Bitmap(this.Width, this.Height);
            Graphics g = Graphics.FromImage(mCoord);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.CompositingQuality = CompositingQuality.HighQuality;
            DrawCoord(g);
            DrawCoord(g);
            g.Dispose();
        }

        internal void UpdateSeriesPoints()
        {
            if (mBatchUpdate)
            {
                mUpdateSeriesPoints = true;
                mUpdateContent = true;
                return;
            }

            foreach (Series se in mSeries)
                se.UpdatePoints();
        }

        internal void UpdateContent()
        {
            if (mBatchUpdate)
            {
                mUpdateContent = true;
                return;
            }

            if (mContent != null)
                mContent.Dispose();
            mContent = null;
            if (this.Width < 1 || this.Height < 1)
                return;

            mContent = new Bitmap(this.Width, this.Height);
            Graphics g = Graphics.FromImage(mContent);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.CompositingQuality = CompositingQuality.HighQuality;
            if (this.Parent != null)
            {
                SolidBrush backBrush = new SolidBrush(this.Parent.BackColor);
                g.FillRectangle(backBrush, 0, 0, this.Width, this.Height);
                backBrush.Dispose();
            }
            if (mCoord != null)
                g.DrawImage(mCoord, 0, 0);
            foreach (Series se in mSeries)
                se.Draw(g, 0, 0);
            g.Dispose();
            //this.Invalidate();
            // 双缓冲绘图
            Graphics draw = this.CreateGraphics();
            draw.DrawImage(mContent, 0, 0);
            draw.Dispose();
        }

        private void Chart_Paint(object sender, PaintEventArgs e)
        {
            try
            {
                if (mContent != null)
                    e.Graphics.DrawImage(mContent, 0, 0);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + ex.StackTrace,
                    "错误",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
            }
        }

        private void Chart_SizeChanged(object sender, EventArgs e)
        {
            try
            {
                UpdateCoordBounds();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + ex.StackTrace,
                    "错误",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
            }
        }

        private void Chart_BackColorChanged(object sender, EventArgs e)
        {
            try
            {
                mBackBrush.Color = this.BackColor;
                UpdateCoord();
                UpdateContent();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + ex.StackTrace,
                    "错误",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
            }
        }

        private void DrawCoord(Graphics g)
        {
            PointF startPos, endPos;

            g.FillRectangle(mBackBrush, 0, 0, this.Width, this.Height);

            // 直角坐标
            if (mCoordType == EPlotType.Line)
            {
                // 背景颜色
                g.FillRectangle(mCoordBackBrush, mLineCoord);

                // X坐标刻度的数量
                int xTickCount = (int)((mAxisX.Maximum - mAxisX.Minimum) / mAxisX.Interval + 1);
                // 绘制X坐标网格线
                for (int i = 0; i < xTickCount; ++i)
                {
                    startPos = CoordToPosition(mAxisX.Minimum + i * mAxisX.Interval, mAxisY.Minimum);
                    endPos = CoordToPosition(mAxisX.Minimum + i * mAxisX.Interval, mAxisY.Maximum);
                    // 绘制X坐标网络线
                    g.DrawLine(mAxisX.GridPen, startPos, endPos);
                    // 绘制X坐标刻度值标签
                    string text = (mAxisX.Minimum + i * mAxisX.Interval).ToString();
                    SizeF size = g.MeasureString(text, this.Font);
                    g.DrawString(text, this.Font, Brushes.Black, startPos.X - size.Width / 2, startPos.Y + 10);
                }
                g.DrawLine(mAxisX.AxisPen,
                    mLineCoord.X,
                    mLineCoord.Y + mLineCoord.Height,
                    mLineCoord.X + mLineCoord.Width,
                    mLineCoord.Y + mLineCoord.Height);

                // Y坐标刻度的数量
                int yTickCount = (int)((mAxisY.Maximum - mAxisY.Minimum) / mAxisY.Interval + 1);
                // 绘制Y坐标网格线
                for (int i = 0; i < yTickCount; ++i)
                {
                    startPos = CoordToPosition(mAxisX.Minimum, mAxisY.Minimum + i * mAxisY.Interval);
                    endPos = CoordToPosition(mAxisX.Maximum, mAxisY.Minimum + i * mAxisY.Interval);
                    // 绘制Y坐标网络线
                    g.DrawLine(mAxisY.GridPen, startPos, endPos);
                    // 绘制Y坐标刻度值标签
                    string text = (mAxisY.Minimum + i * mAxisY.Interval).ToString();
                    SizeF size = g.MeasureString(text, this.Font);
                    g.DrawString(text, this.Font, Brushes.Black, startPos.X - size.Width, startPos.Y - size.Height / 2);
                }
                g.DrawLine(mAxisY.AxisPen,
                    mLineCoord.X,
                    mLineCoord.Y,
                    mLineCoord.X,
                    mLineCoord.Y + mLineCoord.Height);
                // 绘制标记线
                startPos = CoordToPosition(mAxisX.Minimum, mReferLine);
                endPos = CoordToPosition(mAxisX.Maximum, mReferLine);
                g.DrawLine(mReferLinePen, startPos, endPos);
            }
            // 极坐标
            else
            {
                // 圆心坐标、半径长度
                float centerX = mPolarCoord.X + mPolarCoord.Width / 2;
                float centerY = mPolarCoord.Y + mPolarCoord.Height / 2;
                float radius = mPolarCoord.Width / 2;

                startPos = CoordToPosition(mAxisX.Minimum, mAxisY.Minimum);

                // 背景色
                // 必须超出边界1个单位，不然鼠标拾取时由于边界无法完全擦除而留下难看的黑边
                g.FillEllipse(mCoordBackBrush, 
                    mPolarCoord.X - 1,
                    mPolarCoord.Y - 1,
                    mPolarCoord.Width + 2,
                    mPolarCoord.Height + 2);

                // X坐标刻度的数量
                double xTickCount = (mAxisX.Maximum - mAxisX.Minimum) / mAxisX.Interval;
                // 绘制X坐标网格线
                for (int i = 0; i < xTickCount + 1; ++i)
                {
                    endPos = CoordToPosition(mAxisX.Maximum + i * mAxisX.Interval, mAxisY.Maximum);
                    // 绘制X坐标网络线
                    g.DrawLine(mAxisX.GridPen, startPos, endPos);
                    // 绘制X坐标刻度值标签
                    if (endPos.X < centerX)
                    {
                        if (endPos.Y < centerY)
                        {
                            string text = (mAxisX.Minimum + i * mAxisX.Interval).ToString();
                            SizeF size = g.MeasureString(text, this.Font);
                            g.DrawString(text, this.Font, Brushes.Black, endPos.X - size.Width, endPos.Y - size.Height);
                        }
                        else if (endPos.Y == centerY)
                        {
                            string text = (mAxisX.Minimum + i * mAxisX.Interval).ToString();
                            SizeF size = g.MeasureString(text, this.Font);
                            g.DrawString(text, this.Font, Brushes.Black, endPos.X - size.Width, endPos.Y - size.Height / 2);
                        }
                        else
                        {
                            string text = (mAxisX.Minimum + i * mAxisX.Interval).ToString();
                            SizeF size = g.MeasureString(text, this.Font);
                            g.DrawString(text, this.Font, Brushes.Black, endPos.X - size.Width, endPos.Y);
                        }
                    }
                    else if (endPos.X == centerX)
                    {
                        if (endPos.Y < centerY)
                        {
                            string text = (mAxisX.Minimum + i * mAxisX.Interval).ToString();
                            SizeF size = g.MeasureString(text, this.Font);
                            g.DrawString(text, this.Font, Brushes.Black, endPos.X - size.Width / 2, endPos.Y - size.Height);
                        }
                        else
                        {
                            if (i == 0)
                            {
                                string text = mAxisX.Minimum.ToString();
                                SizeF size = g.MeasureString(text, this.Font);
                                g.DrawString(text, this.Font, Brushes.Black, endPos.X - size.Width - 5, endPos.Y);
                            }
                            else
                            {
                                string text = mAxisX.Maximum.ToString();
                                SizeF size = g.MeasureString(text, this.Font);
                                g.DrawString(text, this.Font, Brushes.Black, endPos.X + 5, endPos.Y);
                            }
                        }
                    }
                    else
                    {
                        if (endPos.Y < centerY)
                        {
                            string text = (mAxisX.Minimum + i * mAxisX.Interval).ToString();
                            SizeF size = g.MeasureString(text, this.Font);
                            g.DrawString(text, this.Font, Brushes.Black, endPos.X, endPos.Y - size.Height);
                        }
                        else if (endPos.Y == centerY)
                        {
                            string text = (mAxisX.Minimum + i * mAxisX.Interval).ToString();
                            SizeF size = g.MeasureString(text, this.Font);
                            g.DrawString(text, this.Font, Brushes.Black, endPos.X, endPos.Y - size.Height / 2);
                        }
                        else
                        {
                            string text = (mAxisX.Minimum + i * mAxisX.Interval).ToString();
                            SizeF size = g.MeasureString(text, this.Font);
                            g.DrawString(text, this.Font, Brushes.Black, endPos.X, endPos.Y);
                        }
                    }
                }
                g.DrawEllipse(mAxisX.AxisPen,
                        mPolarCoord.X,
                        mPolarCoord.Y,
                        mPolarCoord.Width,
                        mPolarCoord.Height);

                // Y坐标刻度的数量
                int yTickCount = (int)((mAxisY.Maximum - mAxisY.Minimum) / mAxisY.Interval + 1);
                // Y坐标刻度的角度间隔
                float yStep = radius * mAxisY.Interval / (mAxisY.Maximum - mAxisY.Minimum);
                // 绘制Y坐标网格线
                for (int i = 0; i < yTickCount; ++i)
                {
                    // 绘制Y坐标网络线
                    g.DrawEllipse(mAxisY.GridPen,
                        mPolarCoord.X + (float)(i * yStep),
                        mPolarCoord.Y + (float)(i * yStep),
                        mPolarCoord.Width - (float)(2 * i * yStep),
                        mPolarCoord.Height - (float)(2 * i * yStep));

                    // 绘制Y坐标刻度值标签
                    string text = (mAxisY.Maximum - i * mAxisY.Interval).ToString();
                    SizeF size = g.MeasureString(text, this.Font);
                    g.DrawString(text,
                        this.Font,
                        Brushes.Black,
                        (float)(mPolarCoord.X + i * yStep),
                        (float)centerY);
                }
                g.DrawLine(mAxisY.AxisPen,
                    mPolarCoord.X,
                    mPolarCoord.Y + mPolarCoord.Height / 2,
                    mPolarCoord.X + mPolarCoord.Width / 2,
                    mPolarCoord.Y + mPolarCoord.Height / 2);

                // 绘制标记线
                float offset = (mAxisY.Maximum - mReferLine) * radius / (mAxisY.Maximum - mAxisY.Minimum);
                g.DrawEllipse(mReferLinePen,
                        mPolarCoord.X + offset,
                        mPolarCoord.Y + offset,
                        mPolarCoord.Width - 2 * offset,
                        mPolarCoord.Height - 2 * offset);
            }
        }

        private void UpdateCoordBounds()
        {
            int lineLeft = mCoordPadding;
            int lineTop = mCoordPadding;
            int lineWidth = this.Width - 2 * mCoordPadding - 1;
            int lineHeight = this.Height - 2 * mCoordPadding - 1;
            mLineCoord.X = lineLeft;
            mLineCoord.Y = lineTop;
            mLineCoord.Width = lineWidth;
            mLineCoord.Height = lineHeight;

            int polarWidth = Math.Min(this.Width - 2 * mCoordPadding - 1, this.Height - 2 * mCoordPadding - 1);
            int polarHeight = polarWidth;
            int polarLeft = (this.Width - polarWidth) / 2;
            int polarTop = mCoordPadding;
            mPolarCoord.X = polarLeft;
            mPolarCoord.Y = polarTop;
            mPolarCoord.Width = polarWidth;
            mPolarCoord.Height = polarHeight;

            UpdateCoord();
            UpdateSeriesPoints();
            UpdateContent();
        }
    }
}
